Path: blob/master/src/packages/next/pages/token/[id].tsx
1450 views
/*1* This file is part of CoCalc: Copyright © 2021 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/*6Visit78https://cocalc.com/token/vZmCKcIMha2nKyFQ0rgK910to carry out the action associated with the token vZmCKcIMha2nKyFQ0rgK.11*/1213import Footer from "components/landing/footer";14import Head from "components/landing/head";15import Header from "components/landing/header";16import { Customize, CustomizeType } from "lib/customize";17import withCustomize from "lib/with-customize";18import useAPI from "lib/hooks/api";19import { Alert, Button, Card, Divider, Layout, Space, Spin } from "antd";20import { useRouter } from "next/router";21import type { Description } from "@cocalc/util/db-schema/token-actions";22import { capitalize } from "@cocalc/util/misc";23import { useEffect, useState } from "react";24import { getTokenDescription } from "@cocalc/server/token-actions/handle";25import Markdown from "@cocalc/frontend/editors/slate/static-markdown";26import { Icon, IconName } from "@cocalc/frontend/components/icon";27import getAccountId from "lib/account/get-account";28import InPlaceSignInOrUp from "components/auth/in-place-sign-in-or-up";29import StripePayment from "@cocalc/frontend/purchases/stripe-payment";30import { LineItemsTable } from "@cocalc/frontend/purchases/line-items";31import A from "components/misc/A";3233const STYLE = { margin: "30px auto", maxWidth: "750px", fontSize: "14pt" };3435export async function getServerSideProps(context) {36const { id: token_id } = context.params;37const account_id = await getAccountId(context.req);38let description;39try {40description = await getTokenDescription(token_id, account_id);41} catch (error) {42description = {43type: "error",44title: "Error",45details: `${error}`,46cancelText: "",47okText: "OK",48};49}50return await withCustomize({ context, props: { token_id, description } });51}5253function reloadOnceToCheckForAuth() {54const now = Date.now();55const last = localStorage.reloadOnceToCheckForAuth;56if (last && now - last <= 10000) {57return;58}59localStorage.reloadOnceToCheckForAuth = now;60location.reload();61}6263interface Props {64customize: CustomizeType;65token_id: string;66description: Description & {67title?: string;68details?: string;69okText?: string;70cancelText?: string;71icon?: IconName;72signIn?: boolean;73};74}7576export default function TokenActions({77customize,78description,79token_id,80}: Props) {81const router = useRouter();82const [doAction, setDoAction] = useState<boolean>(false);83const [loading, setLoading] = useState<boolean>(false);84const title = getTitle(description);85useEffect(() => {86// due to samesite auth cookie, we refresh browser once...87// This is needed when clicking a URL from outside cocalc, which is likely.88if (description.signIn) {89reloadOnceToCheckForAuth();90}91}, []);9293return (94<Customize value={customize}>95<Head title={title} />96<Layout>97<Header />98{!!description.signIn && (99<div style={{ marginTop: "30px" }}>100<InPlaceSignInOrUp101title={"Please create an account (which is very easy) or sign in"}102/>103</div>104)}105<Dialog106disabled={doAction || !!description.signIn}107loading={loading}108title={title}109details={description.details}110okText={description.okText}111cancelText={description.cancelText}112icon={description.icon}113payment={description["payment"]}114onConfirm={() => {115setDoAction(true);116}}117onCancel={() => {118setLoading(true);119router.push("/");120}}121/>122{!description.signIn && doAction && <HandleToken token={token_id} />}123<Footer />124</Layout>125</Customize>126);127}128129function Dialog({130disabled,131title,132details,133okText,134cancelText,135icon,136onConfirm,137onCancel,138loading,139payment,140}) {141return (142<Card143style={{144margin: "30px auto",145width: "750px",146maxWidth: "100%",147}}148title={149<Space>150{icon && <Icon name={icon} />}151<Markdown value={title} style={{ marginBottom: "-1em" }} />152</Space>153}154>155{payment && <LineItemsTable lineItems={payment.lineItems} />}156{details && <Markdown value={details} style={{ marginTop: "30px" }} />}157<Divider />158<div style={{ textAlign: "center" }}>159<Space style={{ marginTop: "8px" }}>160{loading && <Spin />}161{cancelText != "" && (162<Button163size="large"164onClick={onCancel}165disabled={disabled || loading}166>167{cancelText ?? "Cancel"}168</Button>169)}170{okText != "" && (171<Button172size="large"173onClick={onConfirm}174disabled={disabled || loading}175type="primary"176>177{okText ?? "Confirm"}178</Button>179)}180</Space>181</div>182</Card>183);184}185186function HandleToken({ token }) {187const { calling, result, error } = useAPI("token-action", { token });188189return (190<div>191{calling && (192<div style={{ ...STYLE, textAlign: "center" }}>193<Spin />194</div>195)}196{error && <Alert showIcon style={STYLE} type="error" message={error} />}197{!calling && result != null && !error && (198<RenderResult data={result.data} />199)}200</div>201);202}203204function RenderResult({ data }) {205const [finishedPaying, setFinishedPaying] = useState<boolean>(false);206207if (data?.pay != null && !finishedPaying) {208return (209<Card style={STYLE} title="Make a Payment">210<div>211<StripePayment212disabled={finishedPaying}213{...data.pay}214onFinished={() => {215setFinishedPaying(true);216}}217/>218</div>219</Card>220);221} else {222return (223<Alert224showIcon225style={STYLE}226type="success"227message={228<>229Thank you for your payment! Please visit{" "}230<A href="/settings/payments">the payments page</A> to ensure your231payment is completed successfully and download a receipt.232</>233}234description={data?.text ? <Markdown value={data?.text} /> : undefined}235/>236);237}238}239240function getTitle({ title, type }: Description & { title?: string }) {241if (title) {242return title;243}244switch (type) {245case "make-payment":246return "Make a Payment";247case "disable-daily-statements":248return "Disable Daily Statements";249default:250if (typeof type == "string" && type) {251return capitalize(type.replace(/-/g, " "));252}253return "Token Action";254}255}256257258